package tcpServer

import (
	"bufio"
	"fmt"
	"image-pull/proto"
	"io"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

// 已注册节点信息
var RegisteredMap sync.Map

// 列出所有连接
func List() ([]net.Conn, bool) {
	var conns []net.Conn
	RegisteredMap.Range(func(key, value interface{}) bool {
		if conn, ok := value.(net.Conn); ok {
			conns = append(conns, conn)
		}
		return true
	})
	return conns, len(conns) > 0
}

// 注册节点
func registered(reader *bufio.Reader, conn net.Conn) {
	recvStr, err := proto.Decode(reader)
	if err != nil {
		if err == io.EOF {
			return
		}
		fmt.Println("decode failed,err:", err)
		return
	}
	//检查节点是否存在
	if _, exist := RegisteredMap.Load(recvStr); !exist {
		RegisteredMap.Store(recvStr, conn)
		fmt.Println("节点注册成功:", recvStr)
	} else {
		fmt.Println("该节点已存在")
	}
}

// 发送数据指令
func Send(conn net.Conn, sendData string) {
	fmt.Println(">>>>>", sendData)
	b, err := proto.Encode(sendData)
	if err != nil {
		fmt.Printf("encode data failed: %v\n", err)
	}
	conn.Write(b)
}

// 注册连接
func Process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	// 注册服务到server端
	registered(reader, conn)
}

func SerInit(listenAddr string) (net.Listener, error) {
	// 启动监听服务器
	listen, err := net.Listen("tcp", listenAddr)
	if err != nil {
		if opErr, ok := err.(*net.OpError); ok && opErr.Err.Error() == "address already in use" {
			fmt.Printf("端口已被占用: %s\n", listenAddr)
		} else {
			fmt.Printf("监听失败，错误: %v\n", err)
		}
		return nil, err
	}
	// 持续监听连接
	var wg sync.WaitGroup
	wg.Add(1)

	done := make(chan os.Signal, 1)
	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

	// 持续监听 提供服务
	go func() {
		defer wg.Done()
		for {
			conn, err := listen.Accept()
			if err != nil {
				fmt.Println("连接失败，错误：", err)
				if isClosedConnError(err) {
					return
				}
				time.Sleep(time.Second)
				continue
			}
			// 把连接信息注册到map中
			go Process(conn)
		}
	}()
	return listen, nil
}

// isClosedConnError 检查错误是否由于连接被关闭
func isClosedConnError(err error) bool {
	nerr, ok := err.(*net.OpError)
	return ok && nerr.Op == "accept" && nerr.Err.Error() == "use of closed file"
}
